package com.xiaoming.core.utils;

import com.xiaoming.core.annotation.Autowired;
import com.xiaoming.core.annotation.Component;
import com.xiaoming.core.annotation.Repository;
import com.xiaoming.core.annotation.Service;
import com.xiaoming.web.dao.AccountDao;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;

public class ClassUtils {
    private Map<String, Object> annotations = new HashMap<>();
    private List<Object> references = new ArrayList<>();

    public Map<String, Object> loadClass(String packageName) {
        Enumeration<URL> resources = null;
        try {
            resources = Thread.currentThread().getContextClassLoader().getResources(packageName.replace(".", "/"));
            while (resources.hasMoreElements()) {
                URL url = resources.nextElement();
                String protocol = url.getProtocol();
                if ("file".equals(protocol)) {
                    String packagePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    this.getClassMap(packagePath, packageName);
                    this.initialzingReference();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return annotations;
    }

    public void getClassMap(String packagePath, String packageName) throws Exception {
        File file = new File(packagePath);
        File[] files = file.listFiles();
        if (files != null) {
            for (File item : files) {
                String fileName = item.getName().replace(".class", "");
                String fullPackageName = packageName + "." + fileName;
                String fullPackagePath = packagePath + "/" + fileName;
                if (item.isFile()) {
                    Class<?> aClass = Class.forName(fullPackageName);
                    Object object = null;
                    Service service = aClass.getAnnotation(Service.class);
                    if (service != null) {
                        object = aClass.getDeclaredConstructor().newInstance();
                        if (service.value().isEmpty()) {
                            this.annotations.put(fileName, object);
                        } else {
                            this.annotations.put(service.value(), object);
                        }
                    }
                    Repository repository = aClass.getAnnotation(Repository.class);
                    if (repository != null) {
                        object = aClass.getDeclaredConstructor().newInstance();
                        if (repository.value().isEmpty()) {
                            this.annotations.put(fileName, object);
                        } else {
                            this.annotations.put(repository.value(), object);
                        }
                    }
                    Component component = aClass.getAnnotation(Component.class);
                    if (component != null) {
                        object = aClass.getDeclaredConstructor().newInstance();
                        this.annotations.put(fileName, object);
                    }

                    this.addReference(object, aClass);
                } else if (item.isDirectory()) {
                    this.getClassMap(fullPackagePath, fullPackageName);
                }
            }
        }
    }

    public void addReference(Object object, Class<?> aClass) throws Exception {
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            if (field.getAnnotation(Autowired.class) != null) {
                if (object == null) {
                    object = aClass.getDeclaredConstructor().newInstance();
                }
                references.add(object);
            }
        }
    }

    public void initialzingReference() {
        annotations.forEach((serviceName, annotation) -> {
            references.forEach(object -> {
                Field[] fields = object.getClass().getDeclaredFields();
                if (fields != null) {
                    for (Field field : fields) {
                        field.setAccessible(true);
                        if (field.getType().equals(annotation.getClass())) {
                            try {
                                field.set(object, annotation);
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            });
        });
    }
}
